Skip to content

Conversation

@Kintaro
Copy link

@Kintaro Kintaro commented Dec 6, 2025

getopt had an issue when parsing optional arguments if they had a space between the opion and its value.

Request: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=291374

Tested with bricoler run freebsd-src-regression-suite

@github-actions
Copy link

github-actions bot commented Dec 6, 2025

Thank you for taking the time to contribute to FreeBSD!
All issues resolved.

@Kintaro
Copy link
Author

Kintaro commented Dec 6, 2025

This probably needs to be backported, but as this is my first contribution I am not sure about the proper workflow on this._

Copy link
Member

@jlduran jlduran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should manually pass the following three tests (at least):

$ ./getoptest -l -w -d14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is 14
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
$ ./getoptest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is 14 # XXX <- (Or is it NULL? I'll have to test on Linux)
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

and

$ ./getoptest -l -w -d -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

if (*place)
optarg = place;
else if (oli[2] == ':')
else if (oli[2] == ':') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The style(9) fixes I was referring to in the bug report should be addressed, maybe using NetBSD style guide (https://cvsweb.netbsd.org/bsdweb.cgi/src/share/misc/style?rev=HEAD&content-type=text/x-cvsweb-markup).
Basically, use tabs, not spaces for indentation, and braces around single-line bodies are optional.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I fixed the tabs. Not sure on the braces. It is a "single instruction" but it is another nested if/else. Not contesting any of it, just unsure which one would be preferred.

As for the tests:

rootnode@x220 ~/c/c/291374> ./getopttest -l -w -d14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is 14
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
rootnode@x220 ~/c/c/291374> ./getopttest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is 14
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
rootnode@x220 ~/c/c/291374> ./getopttest -l -w -d -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, this patch does not match the one from the bug report, nor it passes the tests above.

Copy link
Author

@Kintaro Kintaro Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, this patch does not match the one from the bug report, nor it passes the tests above.

I mixed up the two branches between my 2 machines. One wasn't up to date. You refer to the missing && nargv[optind + 1][0] != '-'?

After some testing it turned out that this would for example segfault on ./getopttest -d -14

At that point it comes down to interpretation I guess. Is the - in the value argument allowed or should it be quoted to be allowed.

The version in this PR is not segfaulting and allows for - values.

$ ./getoptest -l -w -d -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  -s

Should the value 14 be missing as in the example above, then it would stop parsing at that point and return an error.

As I understand it, the man page description allows for the whitespace followed by any value.

But you are correct, the 3rd case would be broken.

I'll see if I can mitigate the segfault and get the 3rd case working. Values starting with a - might have to be quoted then.

Copy link
Member

@jlduran jlduran Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so let's add that 4th test:

./getoptest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  NULL
./getoptest: illegal option -- 1
ch is ? optarg is  NULL
./getoptest: illegal option -- 4
ch is ? optarg is  NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

EDIT: Match the expected FreeBSD output.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean as the expected behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean as the expected behavior?

Yes, unless you have a different suggestion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion makes sense. I will make the appropriate changes. Will take bit due to timezones.

@jlduran
Copy link
Member

jlduran commented Dec 8, 2025

So, I have the following patch you submitted in the Buzgilla Problem Report 291374 (adjusted to match style(9)):

--- a/lib/libc/stdlib/getopt.c
+++ b/lib/libc/stdlib/getopt.c
@@ -107,13 +107,16 @@ getopt(int nargc, char * const nargv[], const char *ostr)
                   entire next argument. */
                if (*place)
                        optarg = place;
-               else if (oli[2] == ':')
+               else if (oli[2] == ':') {
                        /*
                         * GNU Extension, for optional arguments if the rest of
                         * the argument is empty, we return NULL
                         */
-                       optarg = NULL;
-               else if (nargc > ++optind)
+                       if (nargc > optind + 1 && nargv[optind + 1][0] != '-')
+                               optarg = nargv[++optind];
+                       else
+                               optarg = NULL;
+               } else if (nargc > ++optind)
                        optarg = nargv[optind];
                else {
                        /* option-argument absent */

Do you see any issues with it?

@Kintaro
Copy link
Author

Kintaro commented Dec 8, 2025

So, I have the following patch you submitted in the Buzgilla Problem Report 291374 (adjusted to match style(9)):

--- a/lib/libc/stdlib/getopt.c
+++ b/lib/libc/stdlib/getopt.c
@@ -107,13 +107,16 @@ getopt(int nargc, char * const nargv[], const char *ostr)
                   entire next argument. */
                if (*place)
                        optarg = place;
-               else if (oli[2] == ':')
+               else if (oli[2] == ':') {
                        /*
                         * GNU Extension, for optional arguments if the rest of
                         * the argument is empty, we return NULL
                         */
-                       optarg = NULL;
-               else if (nargc > ++optind)
+                       if (nargc > optind + 1 && nargv[optind + 1][0] != '-')
+                               optarg = nargv[++optind];
+                       else
+                               optarg = NULL;
+               } else if (nargc > ++optind)
                        optarg = nargv[optind];
                else {
                        /* option-argument absent */

Do you see any issues with it?

Yes, in it's current state it would segfault on the ./getoptest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0 test. I will have to add a few checks and rethink the approach.

@jlduran
Copy link
Member

jlduran commented Dec 8, 2025

So, I have the following patch you submitted in the Buzgilla Problem Report 291374 (adjusted to match style(9)):

--- a/lib/libc/stdlib/getopt.c
+++ b/lib/libc/stdlib/getopt.c
@@ -107,13 +107,16 @@ getopt(int nargc, char * const nargv[], const char *ostr)
                   entire next argument. */
                if (*place)
                        optarg = place;
-               else if (oli[2] == ':')
+               else if (oli[2] == ':') {
                        /*
                         * GNU Extension, for optional arguments if the rest of
                         * the argument is empty, we return NULL
                         */
-                       optarg = NULL;
-               else if (nargc > ++optind)
+                       if (nargc > optind + 1 && nargv[optind + 1][0] != '-')
+                               optarg = nargv[++optind];
+                       else
+                               optarg = NULL;
+               } else if (nargc > ++optind)
                        optarg = nargv[optind];
                else {
                        /* option-argument absent */

Do you see any issues with it?

Yes, in it's current state it would segfault on the ./getoptest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0 test. I will have to add a few checks and rethink the approach.

That is strange, I don't see any segmentation faults:

$ ./getopttest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  NULL
getopttest: illegal option -- 1
ch is ? optarg is  NULL
getopttest: illegal option -- 4
ch is ? optarg is  NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

@jlduran jlduran added the changes-required Cannot land as is, change requested of submitter label Dec 8, 2025
@Kintaro
Copy link
Author

Kintaro commented Dec 8, 2025

L

So, I have the following patch you submitted in the Buzgilla Problem Report 291374 (adjusted to match style(9)):

--- a/lib/libc/stdlib/getopt.c
+++ b/lib/libc/stdlib/getopt.c
@@ -107,13 +107,16 @@ getopt(int nargc, char * const nargv[], const char *ostr)
                   entire next argument. */
                if (*place)
                        optarg = place;
-               else if (oli[2] == ':')
+               else if (oli[2] == ':') {
                        /*
                         * GNU Extension, for optional arguments if the rest of
                         * the argument is empty, we return NULL
                         */
-                       optarg = NULL;
-               else if (nargc > ++optind)
+                       if (nargc > optind + 1 && nargv[optind + 1][0] != '-')
+                               optarg = nargv[++optind];
+                       else
+                               optarg = NULL;
+               } else if (nargc > ++optind)
                        optarg = nargv[optind];
                else {
                        /* option-argument absent */

Do you see any issues with it?

Yes, in it's current state it would segfault on the ./getoptest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0 test. I will have to add a few checks and rethink the approach.

That is strange, I don't see any segmentation faults:

$ ./getopttest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is  NULL
ch is w optarg is  NULL
ch is d optarg is  NULL
getopttest: illegal option -- 1
ch is ? optarg is  NULL
getopttest: illegal option -- 4
ch is ? optarg is  NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0

I did a clean build. Yes, that seems to work. I will update the PR.

@jlduran
Copy link
Member

jlduran commented Dec 8, 2025

Please commit the patch attached in:

#1918 (comment)

And we'll start from there.

@jlduran jlduran removed the changes-required Cannot land as is, change requested of submitter label Dec 9, 2025
Copy link
Member

@jlduran jlduran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

The correct trailer should be:

PR:<tab><tab>291374

@jlduran jlduran added the ready Seems ready to go, subject to final sanity check label Dec 9, 2025
getopt had an issue when parsing optional arguments if
they had a space between the opion and its value.

Signed-off-by: Simon Wollwage <[email protected]>

PR:		291374
@Kintaro
Copy link
Author

Kintaro commented Dec 9, 2025

Thank you!

The correct trailer should be:

PR:<tab><tab>291374

Updated the commit message. Should hopefully be good to go now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready Seems ready to go, subject to final sanity check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants